#!/usr/bin/python

import os
import sys
import glob
import strict_fsh
import subprocess


PROG = "findcruft"

VERSION = "3.0"

CRUFT_FILTER_DIRECTORIES = ["/usr/libexec/cruft-filters", "/etc/curft-filters"]


c_help = False
c_version = False
c_quiet = False
c_delete = False


def usage():
    print("Find and optionally remove cruft files from the system.")
    print("")
    print("Usage:")
    print("  %s [options]" % (PROG))
    print("")
    print("Options:")
    print("  -h, --help          Show this help message and exit")
    print("  -v, --version       Show program version and exit")
    print("  -q, --quiet         Suppress informational messages")
    print("  --delete            Delete identified cruft files (use with caution)")


def die(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)


def print_info(msg):
    if not c_quiet:
        print(msg)


def parse_arguments():
    global c_help, c_version, c_quiet, c_delete

    i = 1
    while i < len(sys.argv):
        if sys.argv[i] in ["-h", "--help"]:
            c_help = True
            break

        if sys.argv[i] in ["-v", "--version"]:
            c_version = True
            break

        if sys.argv[i] in ["-q", "--quiet"]:
            c_quiet = True
            i += 1
            continue

        if sys.argv[i] == "--delete":
            c_delete = True
            i += 1
            continue

        die("Error: unknown parameter.")


def exec_cruft_filters_one_by_one():
    for cfDir in CRUFT_FILTER_DIRECTORIES:
        for fullfn in glob.glob(os.path.join(cfDir, "*")):
            if not os.path.isdir(fullfn):
                try:
                    yield (fullfn, subprocess.check_output([fullfn], text=True))
                except subprocess.CalledProcessError as e:
                    die("Cruft filter script \"%s\" failed: %s" % (fullfn, e.stderr))


def main():
    print_info(">> Preparing...")
    rootFs = strict_fsh.RootFs()
    wildcards, h = None, None
    if True:
        # get wildcards
        wildcards = rootFs.get_wildcards(wildcards_flag=strict_fsh.WILDCARDS_SYSTEM)

        # filter wildcards: filter layout files
        wildcards = strict_fsh.deduct_wildcards(wildcards, rootFs.get_wildcards(wildcards_flag=strict_fsh.WILDCARDS_LAYOUT))

        # filter wildcards: filter boot files, which we manage by ourself
        wildcards = strict_fsh.deduct_wildcards(wildcards, rootFs.get_wildcards(wildcards_flag=strict_fsh.WILDCARDS_SYSTEM_BOOT))

        # filter wildcards: filter trash files
        wildcards = strict_fsh.deduct_wildcards(wildcards, rootFs.get_wildcards(wildcards_flag=strict_fsh.WILDCARDS_USER_TRASH))

        # filter wildcards: filter by cruft filter scripts
        if True:
            lineSet = dict()
            for scriptFullfn, out in exec_cruft_filters_one_by_one():
                for line in out.split("\n"):
                    if line == "" or line.startswith("#"):
                        continue
                    if line.startswith("~/"):
                        continue

                    if not line.startswith("/"):
                        die("Error: invalid result line for cruft filter script \"%s\"." % (scriptFullfn))

                    # deal with directory symlink, for example:
                    # with /bin -> /usr/bin, we expand wildcard "/bin/abc/**" as ["/bin/abc/**", "/usr/bin/abc/**"], so that we can do a good match
                    tlist = [line]
                    if True:
                        line2 = os.path.join(os.path.realpath(os.path.dirname(line)), os.path.basename(line))
                        if line2 != line:
                            tlist.append(line2)

                    for line in tlist:
                        lineSet["+ " + line] = None
                        while True:
                            line = os.path.dirname(line)
                            lineSet["+ " + line] = None
                            if line == "/":
                                break

            wildcards = strict_fsh.deduct_wildcards(wildcards, lineSet.keys())

        wildcards, h = strict_fsh.finalize_wildcards(wildcards)

        print_info("")

    print_info(">> Searching for cruft files...")
    if True:
        cruftFiles = rootFs.wildcards_glob(wildcards, hints=h)
        cruftFiles.sort()
        print_info("")

    if not c_delete:
        print_info(">> Cruft files found:")
        for scriptFullfn in cruftFiles:
            print_info("    " + scriptFullfn)
    else:
        print_info(">> Deleting cruft files:")
        cruftFiles.reverse()
        for scriptFullfn in cruftFiles:
            print_info("    " + scriptFullfn)
            if os.path.islink(scriptFullfn):
                os.remove(scriptFullfn)
            elif os.path.isdir(scriptFullfn):
                os.rmdir(scriptFullfn)
            else:
                os.remove(scriptFullfn)


if __name__ == "__main__":
    parse_arguments()

    if c_help:
        usage()
        sys.exit(0)

    if c_version:
        print("%s %s" % (PROG, VERSION))
        sys.exit(0)

    main()
